﻿using System.Linq;
using System.Dynamic;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;

namespace Red
{

    public partial class BlackObject : DynamicObject
    {
        string[] Names;
        string[] NamesUC;
        Dictionary<int, BlackObject> objects = new Dictionary<int, BlackObject>();
        string Type;
        BlackObject Parent = null;
        BlackObject Root = null;

        public BlackObject(Stream stream)
        {
            using (var br = new BinaryReader(stream))
            {
                var tmp = br.ReadUInt64();
                var len = br.ReadUInt32();
                var arrlen = br.ReadUInt16();
                var bytes = br.ReadBytes((int)len - 2);
                Names = Encoding.ASCII.GetString(bytes).Split('\0');
                len = br.ReadUInt32();
                arrlen = br.ReadUInt16();
                bytes = br.ReadBytes((int)len - 2);
                NamesUC = Encoding.Unicode.GetString(bytes).Split('\0');
                var index = br.ReadInt32();
                Create(br, index, br.ReadUInt32(), br.BaseStream.Position, null, this);
            }
        }

        long startInFile = 0;

        BlackObject(BinaryReader br, int? index, long len, long start, BlackObject parent, BlackObject root)
        {
            Create(br, index, len, start, parent, root);
        }
        void Create(BinaryReader br, int? index, long len, long start, BlackObject parent, BlackObject root)
        {
            Root = root;
            Parent = parent;
            var typeIndex = br.ReadInt16();

            Type = root.Names[typeIndex];
            if (Type == "TriEventCurve" || index == 95)
            {

            }
            if (index.HasValue)
            {
                root.objects[index.Value] = this;
                dictionary.Add("_i", index);
            }
            dictionary.Add("_t", Type);
            startInFile = start;
            while (br.BaseStream.Position < len + start)
            {
                var nameIndex = br.ReadInt16();
                var name = root.Names[nameIndex];
                if (name == "object" || name == "operator" || name == "")
                {
                    name += '_';
                }
                if (new string[] { "YCurve", "ZCurve", "XCurve" }.Contains(name))
                {

                }
                var key = new MK(Type, name);
                if (Readers.Keys.Contains(key))
                {
                    dictionary.Add(name, Readers[key](br, this, root, null));
                    continue;
                }
                var pos = br.BaseStream.Position;
                var value = br.ReadInt16();
                if (br.BaseStream.Position < len + start)
                {
                    var peek = br.ReadInt16();
                    if (peek == 0)
                    {
                        br.BaseStream.Position = pos;
                        var ind = br.ReadInt32();
                        var notAnArray = new string[] { "mediumDetailMesh", "sourceObject", "destinationObject", "Tr2InstancedMesh", "instanceGeometryResource", "particleSystem", "mesh", "YCurve", "ZCurve", "XCurve" };
                        var sureAnArray = new string[] { "opaqueAreas", "children" };
                        if (!sureAnArray.Contains(name) && ind == root.objects.Last().Key + 1)
                        {
                            dictionary.Add(name, new BlackObject(br, ind, br.ReadUInt32(), br.BaseStream.Position, this, root));
                        }
                        else if (notAnArray.Contains(name)) //not an array so refference
                        {
                            dictionary.Add(name, CreateRef(ind));
                        }
                        else
                        {
                            var arr = new List<dynamic>(ind);
                            dictionary.Add(name, arr);
                            for (int i = 0; i < ind; i++)
                            {
                                var newindex = br.ReadInt32();
                                if (newindex <= root.objects.Last().Key)
                                {
                                    arr.Add(CreateRef(newindex));
                                    continue;
                                }
                                arr.Add(new BlackObject(br, newindex, br.ReadUInt32(), br.BaseStream.Position, this, root));
                            }
                        }
                    }
                    else
                    {
                        dictionary.Add(name, root.Names[value]);
                        br.BaseStream.Position = pos + 2;
                    }
                }
                else
                {
                    if (!dictionary.Keys.Contains(name))
                        dictionary.Add(name, root.Names[value]);
                    else
                    {
                        if (!(Type == "AudEmitterPersisted" && name == "name"))
                            throw new ArgumentException();
                    }
                }
            }
        }

        BlackObject()
        {

        }

        BlackObject CreateRef(int index, bool real = true)
        {
            if (real)
                return Root.objects[index];
            var ret = new BlackObject();
            ret.dictionary["_t"] = "_Ref";
            ret.dictionary["_ri"] = index;
            ret.Root = Root;
            ret.Parent = Parent;
            return ret;
        }


        class MK : Tuple<string, string>
        {
            public MK(string Type, string Name) : base(Type, Name) { }
        }
        class MKF : Tuple<MK, Reader>
        {
            public MKF(MK Key, Reader ReaderFunction) : base(Key, ReaderFunction) { }
            public MKF(string Type, string Name, Reader ReaderFunction) : base(new MK(Type, Name), ReaderFunction) { }
        }
        delegate object Reader(BinaryReader br, BlackObject me, BlackObject root, int? index);


        static readonly Reader Float4Reader = (br, me, root, index) => new float[] { br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle() };
        static readonly Reader Float3Reader = (br, me, root, index) => new float[] { br.ReadSingle(), br.ReadSingle(), br.ReadSingle() };
        static readonly Reader FloatReader = (br, me, root, index) => br.ReadSingle();
        static readonly Reader Int32Reader = (br, me, root, index) => br.ReadInt32();
        static readonly Reader Int322Reader = (br, me, root, index) => new int[] { br.ReadInt32(), br.ReadInt32() };
        static readonly Reader BoolReader = (br, me, root, index) => (br.ReadByte() == 1);
        static readonly Reader NameReader = (br, me, root, index) => root.Names[br.ReadInt16()];
        static readonly Reader NameUCReader = (br, me, root, index) => root.NamesUC[br.ReadInt16()];
        static readonly Reader ObjectWithoutIndexReader = (br, me, root, index) => new BlackObject(br, index, br.ReadInt32(), br.BaseStream.Position, me, root);
        static readonly Reader DebugThrow = (br, me, root, index) => { throw new Exception(); };
        static readonly Reader Matrix4x4Reader = (br, me, root, index) =>
        {
            var matrix = new float[16];
            for (int i = 0; i < 16; i++)
                matrix[i] = br.ReadSingle();
            return matrix;
        };

        static readonly Dictionary<MK, Reader> Readers = new Dictionary<MK, Reader>();

        static readonly MKF[] initialReaders = new MKF[] {
            new MKF("EveSpriteParticleSystem", "color2", Float4Reader),
            new MKF("EveSpriteParticleSystem", "colorMidpoint", FloatReader),
            new MKF("EveSpriteParticleSystem", "velocityStretch", FloatReader),
            new MKF("EveSpriteParticleSystem", "maxParticleCount", Int32Reader),
            new MKF("Tr2ParticleDragForce", "drag", FloatReader),
            new MKF("EveParticleDragForce", "drag", FloatReader),
            new MKF("EveShip2", "boundingSphereRadius", FloatReader),
            new MKF("EveShip2", "boundingSphereCenter", Float3Reader),
            new MKF("EveStation2", "boundingSphereRadius", FloatReader),
            new MKF("EveStation2", "boundingSphereCenter", Float3Reader),
            new MKF("EveRootTransform", "boundingSphereRadius", FloatReader),
            new MKF("EveRootTransform", "boundingSphereCenter", Float3Reader),
            new MKF("TriTexture2DParameter", "useAllOverrides", BoolReader),
            new MKF("TriTexture2DParameter", "mipmapLodBias", FloatReader),
            new MKF("TriTexture2DParameter", "resourcePath", NameUCReader),
            new MKF("TriTexture2DParameter", "addressVMode", Int32Reader),
            new MKF("TriTexture2DParameter", "addressUMode", Int32Reader),
            new MKF("TriTexture2DParameter", "mipFilterMode", Int32Reader),
            new MKF("TriTexture2DParameter", "filterMode", Int32Reader),
            new MKF("TriTexture2DParameter", "maxMipLevel", Int32Reader),
            new MKF("TriTextureCubeParameter", "resourcePath", NameUCReader),
            new MKF("Tr2FloatParameter", "value", FloatReader),
            new MKF("Tr2Vector4Parameter", "value", Float4Reader),
            new MKF("Tr2MeshArea", "index", Int32Reader),
            new MKF("EveSpaceObjectDecal", "position", Float3Reader),
            new MKF("EveSpaceObjectDecal", "scaling", Float3Reader),
            new MKF("EveSpaceObjectDecal", "rotation", Float4Reader),
            new MKF("EveTransform", "scaling", Float3Reader),
            new MKF("EveTransform", "translation", Float3Reader),
            new MKF("EveTransform", "rotation", Float4Reader),
            new MKF("EveTransform", "debugRenderDebugInfoForChildren", BoolReader),
            new MKF("EveTransform", "debugShowBoundingBox", BoolReader),
            new MKF("EveTransform", "modifier", Int32Reader),
            new MKF("EveTransform", "display", BoolReader),
            new MKF("TriTransformParameter", "translation", Float3Reader),
            new MKF("TriTransformParameter", "scaling", Float3Reader),
            new MKF("TriTransformParameter", "transformBase", Int322Reader),
            new MKF("Tr2RandomUniformAttributeGenerator", "minRange", Float4Reader),
            new MKF("Tr2RandomUniformAttributeGenerator", "maxRange", Float4Reader),
            new MKF("Tr2RandomUniformAttributeGenerator", "type", Int32Reader),
            new MKF("EveLocator2", "transform", Matrix4x4Reader), 
            new MKF("Tr2ParticleSystem","maxParticleCount", Int32Reader),
            new MKF("Tr2DynamicEmitter", "rate", FloatReader),
            new MKF("Tr2ParticleElementDeclaration", "type", Int32Reader),
            new MKF("Tr2ParticleElementDeclaration", "usedByGPU", BoolReader),
            new MKF("Tr2ParticleElementDeclaration", "usageIndex", Int32Reader),
            new MKF("Tr2ParticleElementDeclaration", "dimension", Int32Reader),
            new MKF("Tr2InstancedMesh", "minBounds", Float3Reader),
            new MKF("Tr2InstancedMesh", "maxBounds", Float3Reader),
            new MKF("EveSpriteSetItem", "blinkPhase", FloatReader),
            new MKF("EveSpriteSetItem", "position", ObjectWithoutIndexReader),
            new MKF("EveSpriteSetItem", "color", ObjectWithoutIndexReader),
            new MKF("EveShipAnimCurve", "gr2BoneIndex", Int32Reader),
            new MKF("TriColorCurve", "start", Int322Reader),
            new MKF("TriColorCurve", "length", FloatReader),
            new MKF("TriColorCurve", "value", ObjectWithoutIndexReader),
            new MKF("TriColorCurve", "extrapolation", Int32Reader),
            new MKF("TriColorKey", "interpolation", Int32Reader),
            new MKF("TriColorKey", "time", FloatReader),
            new MKF("TriColorKey", "value", ObjectWithoutIndexReader),
            new MKF("TriColorKey", "left", ObjectWithoutIndexReader),
            new MKF("TriColorKey", "right", ObjectWithoutIndexReader),
            new MKF("TriSineCurve", "value", FloatReader),
            new MKF("TriSineCurve", "offset", FloatReader),
            new MKF("TriSineCurve", "scale", FloatReader),
            new MKF("TriSineCurve", "speed", FloatReader),
            new MKF("TriVectorCurve", "start", Int322Reader),
            new MKF("TriVectorCurve", "length", FloatReader),
            new MKF("TriVectorCurve", "value", ObjectWithoutIndexReader),
            new MKF("TriVectorCurve", "extrapolation", Int32Reader),
            new MKF("TriVectorKey", "interpolation", Int32Reader),
            new MKF("TriVectorKey", "time", FloatReader),
            new MKF("TriVectorKey", "value", ObjectWithoutIndexReader),
            new MKF("TriVectorKey", "left", ObjectWithoutIndexReader),
            new MKF("TriVectorKey", "right", ObjectWithoutIndexReader),
            new MKF("TriScalarCurve", "start", Int322Reader),
            new MKF("TriScalarCurve", "length", FloatReader),
            new MKF("TriScalarCurve", "value", Int32Reader),
            new MKF("TriScalarCurve", "extrapolation", Int32Reader),
            new MKF("TriScalarKey", "interpolation", Int32Reader),
            new MKF("TriScalarKey", "time", FloatReader),
            new MKF("TriScalarKey", "value", FloatReader),
            new MKF("TriScalarKey", "left", FloatReader),
            new MKF("TriScalarKey", "right", FloatReader),
            new MKF("Tr2EmitterSpherical", "atlasTextureCount", Int32Reader),
            new MKF("Tr2EmitterSpherical", "position", Float3Reader),
            new MKF("Tr2EmitterSpherical", "rotation", Float4Reader),
            new MKF("TriScalarSequencer", "value", FloatReader),
            new MKF("TriValueBinding", "scale", FloatReader),
            new MKF("TriYPRSequencer", "value", ObjectWithoutIndexReader),
            new MKF("TriYPRSequencer", "YawPitchRoll", ObjectWithoutIndexReader),
            new MKF("TriXYZScalarSequencer", "value", ObjectWithoutIndexReader),
            new MKF("TriFloatParameter", "value", FloatReader),
            new MKF("TriRotationCurve", "start", Int322Reader),
            new MKF("TriRotationCurve", "length", FloatReader),
            new MKF("TriRotationCurve", "value", ObjectWithoutIndexReader),
            new MKF("TriRotationCurve", "extrapolation", Int32Reader),
            new MKF("TriQuaternionKey", "interpolation", Int32Reader),
            new MKF("TriQuaternionKey", "time", FloatReader),
            new MKF("TriQuaternionKey", "value", ObjectWithoutIndexReader),
            new MKF("TriQuaternionKey", "left", ObjectWithoutIndexReader),
            new MKF("TriQuaternionKey", "right", ObjectWithoutIndexReader),
            new MKF("TriEventKey", "time", FloatReader),
            new MKF("TriPerlinCurve", "N", Int32Reader),
        };

        static void AddReaders(string[] properties, string Type, Reader reader)
        {
            foreach (var s in properties)
                Readers.Add(new MK(Type, s), reader);
        }

        static BlackObject()
        {
            initialReaders.ToList().ForEach(mkf => Readers.Add(mkf.Item1, mkf.Item2));
            AddReaders(new string[] { "blinkRate", "minScale", "maxScale", "falloff" }, "EveSpriteSetItem", FloatReader);
            var VectorOrQuaternionProperties = new string[] { "x", "y", "z", "w" };
            AddReaders(VectorOrQuaternionProperties, "TriQuaternion", FloatReader);
            AddReaders(VectorOrQuaternionProperties, "TriVector", FloatReader);
            AddReaders(new string[] { "v1", "v2", "v3", "v4" }, "TriVector4Parameter", FloatReader);
            AddReaders(new string[] { "a", "r", "g", "b" }, "TriColor", FloatReader);
            AddReaders(new string[] { "minPhi", "maxPhi", "maxTheta", "minSpeed", "maxSpeed", "maxRadius" }, "Tr2SphereShapeAttributeGenerator", FloatReader);
            AddReaders(new string[] { "spawnRate", "minPhi", "maxPhi", "minTheta", "maxTheta", "minLifetime", "maxLifetime", "maxRotationInit", "minRotationVelo", "maxRotationVelo", "minSizeBirth", "maxSizeBirth", "minSizeDeath", "maxSizeDeath", "minVelocity", "maxVelocity" }, "Tr2EmitterSpherical", FloatReader);
            AddReaders(new string[] { "value", "offset", "scale", "alpha", "speed", "beta" }, "TriPerlinCurve", FloatReader);
        }

        Dictionary<string, object> dictionary = new Dictionary<string, object>();

        public int Count { get { return dictionary.Count; } }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return dictionary.TryGetValue(binder.Name, out result);
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            dictionary[binder.Name] = value;
            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return dictionary.Keys;
        }

        public override string ToString()
        {
            return this.Type;
        }
    }
}
